"""
小兵小兵电子版

一个基于Python的"小兵小兵"游戏的逻辑实现。

主要模块：
- GameRules: 游戏规则配置类
- OperationType: 操作类型枚举
- GameState: 游戏状态管理
- Player/DoubleHandPlayer: 玩家类
- Fight: 战斗逻辑处理
- GameHandler: 游戏核心逻辑处理器
- Game: 游戏主控制器

使用示例：
>>> game = Game(STANDARD_RULES)
>>> game.add_player("玩家1")
>>> game.add_player("玩家2", is_ai=True)
>>> game.add_operation("玩家1", Operation(OperationType.LOAD))
>>> game.step()

版本: 1.0
作者: 高尔基
日期: 2025.10.25
"""

import itertools
from dataclasses import dataclass
from enum import Enum
from random import choice, choices
from typing import Optional, List, Final


@dataclass(frozen=True)
class GameRules:
    """游戏规则配置类"""
    allow_multiple_targets: bool  # 允许多目标
    enable_blasts: bool  # 多样化炸膛
    enable_self_attacks: bool  # 打自己
    enable_advanced_merging: bool  # 高级合并
    enable_basic_merging: bool = True  # 基础合并
    enable_taunts: bool = True  # 嘲讽
    enable_multiple_taunts: bool = False  # 多样化嘲讽

ALL_RULES: Final[GameRules] = GameRules(allow_multiple_targets=True,
                                          enable_blasts=True,
                                          enable_self_attacks=True,
                                          enable_advanced_merging=True,
                                          enable_taunts=True,
                                          enable_multiple_taunts=True)

STANDARD_RULES: Final[GameRules] = GameRules(allow_multiple_targets=True,
                                             enable_blasts=True,
                                             enable_self_attacks=True,
                                             enable_advanced_merging=True)

SIMPLIFIED_RULES: Final[GameRules] = GameRules(allow_multiple_targets=False,
                                               enable_blasts=False,
                                               enable_self_attacks=False,
                                               enable_advanced_merging=False)


class OperationType(Enum):
    """操作类型枚举"""
    NONE = "无"
    LOAD = "装弹"
    DEFENCE = "防御"
    ATTACK = "开枪"
    TAUNT = "嘲讽"
    TAUNT_YEAH = "yeah~嘲讽"
    TAUNT_FAKE_5 = "假5发嘲讽"
    DOUBLE_LOAD = "连装"
    DOUBLE_DEFENCE = "双防"


@dataclass(frozen=True)
class GameState:
    """游戏状态类

    :param rules: 游戏规则配置
    :param turn: 当前回合数
    :param players: 所有玩家列表
    :param active_players: 当前活跃玩家列表
    :param ending: 游戏是否结束
    :param winner: 游戏结束时获胜的玩家，为False表示游戏结束但谁都没赢
    """
    rules: GameRules
    turn: int
    players: set["Player"]
    active_players: set["Player"]
    double_hand_players: set["DoubleHandPlayer"]
    double_hand_active_players: set["DoubleHandPlayer"]
    ending: bool = False
    winner: Optional["Player"] = None

    def __repr__(self):
        return (f"GameState({self.active_players}|{self.double_hand_active_players}, "
                f"ending={self.ending}, winner={self.winner})")


@dataclass(frozen=True)
class Operation:
    """玩家操作类

    :param type: 操作类型
    :param attacks: 选择开枪类型时有值: {目标玩家: 弹药数}
    """
    type: OperationType
    attacks: Optional[dict["Player", int]] = None

    def __hash__(self):
        return hash((self.type, tuple(self.attacks.items())))


class DoubleHandPlayer:
    """双手玩家类，表示游戏中的一个双手玩家"""

    _next_id = 0

    def __init__(self, name: str, is_ai: bool = False, shared_ammo: bool = False):
        self.id = DoubleHandPlayer._next_id
        DoubleHandPlayer._next_id += 1

        self.name = name
        self.alive = True
        self.is_ai = is_ai
        self.shared_ammo = shared_ammo  # 是否共享弹药池
        # 创建双手玩家 - 使用列表包装弹药值以实现真正的共享
        if shared_ammo:
            # 使用列表包装弹药值，这样两只手可以共享同一个可变对象
            shared_ammo_container = [0]  # 列表是可变对象
            self.left_hand = Player(f"{name}-Left", is_ai=is_ai, ammo_container=shared_ammo_container)
            self.right_hand = Player(f"{name}-Right", is_ai=is_ai, ammo_container=shared_ammo_container)
        else:
            # 不共享弹药，每只手有自己的弹药
            self.left_hand = Player(f"{name}-Left", is_ai=is_ai)
            self.right_hand = Player(f"{name}-Right", is_ai=is_ai)
        # 设置双手关系
        self.left_hand.master = self
        self.right_hand.master = self
        self.left_hand.other_hand = self.right_hand
        self.right_hand.other_hand = self.left_hand

    def __repr__(self):
        if self.shared_ammo:
            # 在共享模式下，两只手的弹药是相同的
            ammo = self.left_hand.ammo
            return (f"{self.name}[共享:{ammo}弹, "
                    f"左{'活' if self.left_hand.alive else '死'}, "
                    f"右{'活' if self.right_hand.alive else '死'}]")
        hands_status = []
        if self.left_hand:
            hands_status.append(f"左:{self.left_hand.ammo}弹{'活' if self.left_hand.alive else '死'}")
        if self.right_hand:
            hands_status.append(f"右:{self.right_hand.ammo}弹{'活' if self.right_hand.alive else '死'}")
        return f"{self.name}[{', '.join(hands_status)}]"

    def __hash__(self):
        return hash(self.id)


class Player:
    """玩家类，表示游戏中的一个玩家"""

    _next_id = 0  # 玩家ID计数器

    def __init__(self, name: str,
                 operation: Operation = None,
                 ammo: int = 0,
                 ammo_container: list = None,
                 alive: bool = True,
                 is_ai: bool = False):
        self.id = Player._next_id
        Player._next_id += 1

        if operation is None:
            operation = Operation(OperationType.NONE)

        self.name = name
        self.operation = operation
        self.alive = alive
        self.is_invincible = False
        self.is_ai = is_ai
        self.master = None  # 所属的双手玩家
        self.other_hand = None  # 另一只手

        # 关键修改：使用弹药容器或独立弹药
        if ammo_container is not None:
            # 共享弹药模式：使用共享的弹药容器
            self._ammo_container = ammo_container
        else:
            # 独立弹药模式：创建独立的弹药容器
            self._ammo_container = [ammo]

    @property
    def ammo(self):
        """获取弹药数量"""
        return self._ammo_container[0]

    @ammo.setter
    def ammo(self, value):
        """设置弹药数量"""
        self._ammo_container[0] = value

    def execute_ammo_operation(self):
        """执行弹药操作"""
        if self.operation.type == OperationType.LOAD:
            self.ammo += 1
        elif self.operation.type == OperationType.ATTACK:
            attack_sum = self.get_attack_sum()
            self.ammo -= attack_sum

    def get_attack_sum(self) -> int:
        """获取玩家实际使用的攻击弹药总数"""
        result = 0
        for ammo in self.operation.attacks.values():
            result += ammo
        return result

    def validate_have_operation(self):
        """检查玩家是否有操作"""
        if self.operation.type == OperationType.NONE:
            raise ValueError(f"{self.name} doesn't have any operation")

    def validate_enough_ammo(self):
        """检查玩家是否有足够的弹药"""
        if self.operation.type == OperationType.ATTACK:
            actual_ammo = self.ammo
            if actual_ammo < self.get_attack_sum():
                raise ValueError(f"{self.name} doesn't have enough ammo for attack "
                                 f"(has {actual_ammo}, needs {self.get_attack_sum()})")

    def validate_multiple_targets(self):
        """检查玩家是否有多个目标"""
        if len(self.operation.attacks) > 1:
            raise ValueError(f"{self.name} can only attack one target")

    def validate_self_attack(self):
        """检查玩家是否打自己"""
        for target in self.operation.attacks.keys():
            if target == self:
                raise ValueError(f"{self.name} can't attack itself")

    def validate_blasts(self):
        """检查玩家是否有炸膛（超过4弹）操作"""
        for ammo in self.operation.attacks.values():
            if ammo >= 4:
                raise ValueError(f"{self.name} can't use more than 4 ammo for blasts")

    def validate_taunts(self):
        """检查玩家是否有嘲讽操作"""
        if self.operation.type == OperationType.TAUNT:
            raise ValueError(f"{self.name} can't use taunt operation")

    def validate_multiple_taunts(self):
        """检查玩家是否有多样化嘲讽操作"""
        if self.operation.type in (OperationType.TAUNT_FAKE_5, OperationType.TAUNT_YEAH):
            raise ValueError(f"{self.name} can't use multiple taunts")

    def __repr__(self):
        status = "活" if self.alive else "死"
        op_str = self.operation.type.value
        actual_ammo = self.ammo
        return (f"{self.name}"
                f"[{actual_ammo}弹"
                f"{',AI' if self.is_ai else ''}"
                f"{',' + op_str if op_str else ''}"
                f"{',无敌' if self.is_invincible else ''}] "
                f"{status}")

    def __hash__(self):
        return hash(self.id)


class SelfAttackPlayer(Player):
    """打自己时创建的临时玩家类，继承自Player

    :param original_player: 原始玩家对象
    :param ammo_used: 使用的弹药数量
    """

    def __init__(self, original_player: Player, ammo_used: int):
        super().__init__(f"{original_player.name}'",
                         Operation(OperationType.ATTACK),
                         ammo_used,
                         True,
                         is_ai=original_player.is_ai)
        self.original_player = original_player
        self.ammo_used = ammo_used

    def __repr__(self):
        return f"{self.name}[{self.ammo}弹,打自己{self.original_player.name}] {'活' if self.alive else '死'}"


class Fight:
    """战斗类，表示两个玩家集合之间的战斗

    :param first: 第一方玩家集合
    :param first_ammo: 第一方使用的总弹药
    :param first_alive: 第一方是否存活
    :param first_defence: 第一方是否防御
    :param second: 第二方玩家集合
    :param second_ammo: 第二方使用的总弹药
    :param second_alive: 第二方是否存活
    :param second_defence: 第二方是否防御
    """

    def __init__(self, first=None, first_ammo: int = 0, first_alive: Optional[bool] = None, first_defence: bool = False,
                 second=None, second_ammo: int = 0, second_alive: Optional[bool] = None, second_defence: bool = False):
        if first is None:
            first = set()
        if second is None:
            second = set()
        self.first = first
        self.first_ammo = first_ammo
        self.first_alive = first_alive
        self.first_defence = first_defence
        self.second = second
        self.second_ammo = second_ammo
        self.second_alive = second_alive
        self.second_defence = second_defence

    def swapped(self):
        """返回交换了攻防双方的战斗副本

        :return: 交换双方后的新战斗对象
        """
        return Fight(
            first=self.second,
            first_ammo=self.second_ammo,
            first_alive=self.second_alive,
            first_defence=self.second_defence,
            second=self.first,
            second_ammo=self.first_ammo,
            second_alive=self.first_alive,
            second_defence=self.first_defence
        )

    def handle_attack(self):
        """处理攻击"""
        # 炸膛不判断
        if self.first_ammo >= 4 or self.second_ammo >= 4:
            return
        # 0v0不判断
        if self.first_ammo == self.second_ammo == 0:
            return
        # 核弹
        if self.first_ammo == 3 and self.second_ammo == 3:
            self.first_alive = False
            self.second_alive = False
            return
        if self.first_ammo == 3:
            self.second_alive = False
            return
        if self.second_ammo == 3:
            self.first_alive = False
            return
        # 双打单双死
        if self.first_ammo == 2 and self.second_ammo == 1:
            self.first_alive = False
            return
        if self.first_ammo == 1 and self.second_ammo == 2:
            self.second_alive = False
            return
        # 互相伤害
        if self.first_ammo == self.second_ammo:
            self.first_alive = False
            self.second_alive = False
            return
        # 直接伤害
        if self.first_ammo >= 1 and not self.second_defence:
            self.second_alive = False
            return
        if self.second_ammo >= 1 and not self.first_defence:
            self.first_alive = False
            return
        # 防御逻辑
        if self.first_ammo >= 2 and self.second_defence:
            self.second_alive = False
            return
        if self.second_ammo >= 2 and self.first_defence:
            self.first_alive = False
            return

    def __repr__(self):
        first_names = "".join(p.name for p in self.first)
        second_names = "".join(p.name for p in self.second)
        return (f"("
                f"{first_names}"
                f"{self.first_ammo if self.first_ammo else ''}"
                f"{'防' if self.first_defence else ''}"
                f", "
                f"{second_names}"
                f"{self.second_ammo if self.second_ammo else ''}"
                f"{'防' if self.second_defence else ''}"
                f")")

    def __hash__(self):
        return hash((tuple(self.first), self.first_ammo, self.first_alive, self.first_defence,
                     tuple(self.second), self.second_ammo, self.second_alive, self.second_defence))


class GameHandler:
    """游戏处理类，负责处理游戏中的逻辑

    :param game_state: 游戏状态对象
    """

    def __init__(self, game_state: GameState):
        self.rules = game_state.rules
        self.turn = game_state.turn
        self.players = game_state.players
        self.active_players = game_state.active_players
        self.double_hand_players = game_state.double_hand_players
        self.double_hand_active_players = game_state.double_hand_active_players
        self.ending = False
        self.winner = None
        self.fights: set[Fight] = set()

    def calculate(self):
        """一键处理所有战斗逻辑，按顺序执行各个处理步骤"""
        self.turn = self.turn + 1 if self.turn > 0 else 1
        self.ai_action()  # AI行动
        self.check_errors()  # 检查错误
        self.handle_ammo_operation()  # 处理弹药操作
        self.create_fights()  # 创建战斗
        self.merge_fights()  # 合并战斗
        self.handle_attacks()  # 处理伤害
        self.handle_blasts()  # 处理炸膛
        self.handle_self_attacks()  # 处理打自己
        self.handle_taunts()  # 处理嘲讽
        self.handle_dead_players()  # 处理死亡玩家
        self.handle_invincible()  # 处理无敌状态
        self.remove_dead_players()  # 移除死亡玩家
        self.remove_invincible_token()  # 移除无敌标记
        self.check_win()  # 检查胜利
        return self.export_game_state()

    def ai_action(self):
        """AI执行操作"""
        for p in self.active_players:
            if p.is_ai:
                p.operation = self._ai_decision(p)

    def check_errors(self):
        """检查各类错误"""
        for p in self.active_players:
            p.validate_have_operation()  # 必须有操作
            p.validate_enough_ammo()  # 必须有足够弹药
            if not self.rules.allow_multiple_targets:
                p.validate_multiple_targets()  # 必须只有一个目标
            if not self.rules.enable_self_attacks:
                p.validate_self_attack()  # 不能打自己
            if not self.rules.enable_blasts:
                p.validate_blasts()  # 不能炸膛
            if not self.rules.enable_taunts:
                p.validate_taunts()  # 不能嘲讽
            if not self.rules.enable_multiple_taunts:
                p.validate_multiple_taunts()  # 不能多样化嘲讽

    def _ai_decision(self, player: Player) -> Operation:
        """AI决策函数"""
        if self.turn <= 1:  # 第一回合装弹
            return Operation(OperationType.LOAD)
        current_ammo = player.ammo
        # 根据弹药数量调整操作概率
        if current_ammo == 0:
            # 0发弹药：装弹概率增加
            weights = [0.7, 0.3, 0]  # 装弹, 防御
        elif 1 <= current_ammo <= 2:
            # 1-2发弹药：正常概率
            weights = [0.3, 0.3, 0.4]  # 装弹, 防御, 攻击
        else:
            # 3发以上：装弹和防御概率减少，纯攻击增加
            attack_weight = min(0.9, 0.5 + (current_ammo - 3) * 0.1)  # 每多发弹药增加10%攻击倾向
            remaining = 1.0 - attack_weight
            # 剩余概率平分给装弹和防御
            load_weight = remaining / 2
            defence_weight = remaining / 2
            weights = [load_weight, defence_weight, attack_weight]
        # 根据权重随机选择操作类型
        operations = [OperationType.LOAD, OperationType.DEFENCE, OperationType.ATTACK]
        operation_choice = choices(operations, weights=weights)[0]
        if operation_choice == OperationType.ATTACK:
            valid_attack_operations = self._get_valid_attack_operations(player)
            if valid_attack_operations:
                return choice(list(valid_attack_operations))
            # 如果没有有效攻击操作，回退到装弹或防御
            return choice([Operation(OperationType.LOAD), Operation(OperationType.DEFENCE)])
        return Operation(operation_choice)

    def _get_valid_attack_operations(self, player: Player) -> set[Operation]:
        """获取所有有效攻击操作集合"""
        actual_ammo = player.ammo
        if actual_ammo == 0:  # 0弹打不了
            return set()
        valid_operations = set()

        # 只选择存活的玩家作为目标
        target_players = [p for p in self.active_players if p.alive and p != player]

        # 如果没有存活的目标玩家，返回空集合
        if not target_players:
            return set()

        # 打一个目标
        for ammo in range(1, actual_ammo + 1):
            for p in target_players:
                valid_operations.add(Operation(OperationType.ATTACK, {p: ammo}))

        # 打多个目标
        if not self.rules.allow_multiple_targets:  # 不允许多目标
            return valid_operations
        if actual_ammo < 2:  # 提前返回不能打多个的
            return valid_operations

        for num_targets in range(2, min(actual_ammo, len(target_players)) + 1):
            # 选择目标组合
            for targets in itertools.combinations(target_players, num_targets):
                ammo_allocations = self._split_ammo_to_targets(actual_ammo, num_targets)
                for allocation in ammo_allocations:
                    attacks_dict = {}
                    for i, target in enumerate(targets):
                        attacks_dict[target] = allocation[i]
                    valid_operations.add(Operation(OperationType.ATTACK, attacks_dict))

        return valid_operations

    def _split_ammo_to_targets(self, total_ammo: int, num_targets: int) -> List[List[int]]:
        """将总弹药数拆分成num_targets份，每份至少1弹

        :param total_ammo: 总弹药数
        :param num_targets: 目标数量
        :return: 所有可能的分配方案列表
        """
        if num_targets > total_ammo:
            return []

        # 使用递归方法生成所有可能的分配
        def generate_splits(remaining: int, parts: int, current: List[int], results: List[List[int]]):
            if parts == 1:
                results.append(current + [remaining])
                return
            for i in range(1, remaining - parts + 2):
                generate_splits(remaining - i, parts - 1, current + [i], results)

        results = []
        generate_splits(total_ammo, num_targets, [], results)
        return results

    def handle_ammo_operation(self):
        """处理弹药操作"""
        for p in self.active_players:
            p.execute_ammo_operation()

    def create_fights(self):
        """根据玩家攻击操作创建初始战斗"""
        for player in self.active_players:
            if player.operation.type == OperationType.ATTACK:
                for target, ammo in player.operation.attacks.items():
                    if target == player:  # 打自己
                        self.fights.add(Fight(
                            first={SelfAttackPlayer(player, ammo)},
                            first_ammo=ammo,
                            first_alive=True,
                            first_defence=False,
                            second={player},
                            second_ammo=0,
                            second_alive=True,
                            second_defence=False,  # 打自己肯定不会防御
                        ))
                    else:  # 普通攻击
                        self.fights.add(Fight(
                            first={player},
                            first_ammo=ammo,
                            first_alive=True,
                            first_defence=False,
                            second={target},
                            second_ammo=0,
                            second_alive=True,
                            second_defence=target.operation.type == OperationType.DEFENCE,
                        ))

    def merge_fights(self):
        """整理并合并相似的战斗"""
        if self.rules.enable_basic_merging:  # 基础合并
            merged_basic_fights = self._merge_fights(self._can_merge_basic)
            self.fights = merged_basic_fights
        if self.rules.enable_advanced_merging:  # 高级合并
            merged_advanced_fights = self._merge_fights(self._can_merge_advanced)
            self.fights = merged_advanced_fights

    def _merge_fights(self, can_merge_func) -> set[Fight]:
        """通战斗合并函数

        :param can_merge_func: 合并判断函数 (_can_merge_basic 或 _can_merge_advanced)
        :return: 合并后的战斗集合
        """
        fights_list = list(self.fights)
        merged_fights = []
        used_fights = set()  # 记录已经被合并的战斗索引
        for i, fight in enumerate(fights_list):
            if i in used_fights:
                continue
            merged = False

            # 检查是否与已有战斗可以合并
            for j, existing_fight in enumerate(merged_fights):
                # 尝试三种可能的合并方式
                if can_merge_func(fight, existing_fight):
                    merged_fight = self._merge_fight(fight, existing_fight)
                    merged_fights[j] = merged_fight
                    used_fights.add(i)
                    merged = True
                    break
                elif can_merge_func(fight.swapped(), existing_fight):
                    merged_fight = self._merge_fight(fight.swapped(), existing_fight)
                    merged_fights[j] = merged_fight
                    used_fights.add(i)
                    merged = True
                    break
                elif can_merge_func(fight, existing_fight.swapped()):
                    merged_fight = self._merge_fight(fight, existing_fight.swapped())
                    merged_fights[j] = merged_fight
                    used_fights.add(i)
                    merged = True
                    break
            if not merged:
                merged_fights.append(fight)
        return set(merged_fights)

    def _can_merge_basic(self, fight1: Fight, fight2: Fight) -> bool:
        """判断两个战斗是否可以普通合并
        条件：重叠目标，重叠攻击方
        例：(A1, B)+(B2, A)=(A1, B2)
        """
        if fight1.first == fight2.first and fight1.second == fight2.second:
            return True
        return False

    def _can_merge_advanced(self, fight1: Fight, fight2: Fight) -> bool:
        """判断两个战斗是否可以高级合并
        条件：相同目标，不同攻击者，且至少有一个战斗的目标弹药为0

        例1：(A1, C)+(B1, C)=(AB2, C)  # 目标C弹药为0
        例2：(A1, C1)+(B1, C)=(AB2, C1)  # 第二个战斗的目标C弹药为0
        例3：(A'1, A)+(B1, A1)=(A'B2, A1)  # 第一个战斗的目标A弹药为0

        反例：(A2, C1)+(B1, C1)不能合并  # 两个战斗的目标弹药都不为0
        """
        if fight1.first_ammo == 0 and fight2.first_ammo != 0:
            return False
        if fight1.first_ammo != 0 and fight2.first_ammo == 0:
            return False
        if fight1.second_ammo != 0 and fight2.second_ammo != 0:
            return False
        if fight1.second != fight2.second:
            return False
        return True

    def _merge_fight(self, fight1: Fight, fight2: Fight) -> Fight:
        """合并两个战斗"""
        return Fight(
            first=fight1.first | fight2.first,
            first_ammo=fight1.first_ammo + fight2.first_ammo,
            first_alive=fight1.first_alive and fight2.first_alive,  # 存疑，在真正合并的时候不能合并又活又死的
            first_defence=fight1.first_defence or fight2.first_defence,  # 存疑，在真正合并的时候不能合并又防又不防的
            second=fight1.second | fight2.second,
            second_ammo=fight1.second_ammo + fight2.second_ammo,
            second_alive=fight1.second_alive and fight2.second_alive,
            second_defence=fight1.second_defence or fight2.second_defence,
        )

    def handle_attacks(self):
        """处理伤害逻辑"""
        for f in self.fights:
            f.handle_attack()

    def handle_blasts(self):
        """处理炸膛逻辑"""
        for f in self.fights:
            self._handle_blast_4(f)
            self._handle_blast_5(f)
            self._handle_blast_6(f)
            self._handle_blast_10(f)

    def _handle_blast_4(self, fight: Fight):
        """处理4发炸膛：攻击者死"""
        if fight.first_ammo == 4:
            fight.first_alive = False
        if fight.second_ammo == 4:
            fight.second_alive = False

    def _handle_blast_5(self, fight: Fight):
        """处理5发炸膛，除了攻击者死，攻击者无敌"""
        if fight.first_ammo == 5:
            # 除了攻击者死
            for p in self.players - fight.first:
                p.alive = False
            # 攻击者无敌，打自己玩家的原始玩家也无敌
            for p in fight.first:
                p.is_invincible = True
                if isinstance(p, SelfAttackPlayer):
                    p.original_player.is_invincible = True
        if fight.second_ammo == 5:
            # 除了攻击者死
            for p in self.players - fight.second:
                p.alive = False
            # 攻击者无敌，打自己玩家的原始玩家也无敌
            for p in fight.second:
                p.is_invincible = True
                if isinstance(p, SelfAttackPlayer):
                    p.original_player.is_invincible = True

    def _handle_blast_6(self, fight: Fight):
        """处理6发炸膛，都死"""
        if 6 <= fight.first_ammo < 10:
            for p in self.players:
                p.alive = False
        if 6 <= fight.second_ammo < 10:
            for p in self.players:
                p.alive = False

    def _handle_blast_10(self, fight: Fight):
        """处理10发炸膛，都无敌"""
        if fight.first_ammo >= 10:
            for p in self.players:
                p.is_invincible = True
        if fight.second_ammo >= 10:
            for p in self.players:
                p.is_invincible = True

    def handle_self_attacks(self):
        """处理打自己逻辑"""
        # 逻辑：如果打自己玩家死亡但原始玩家存活，移除打自己玩家
        for fight in self.fights:
            self_attack_player_location = None  # 记录打自己玩家位置，'first'/'second'/None
            self_attack_player = None  # 记录打自己玩家对象，没有就是None
            for player in fight.first:
                if isinstance(player, SelfAttackPlayer):
                    self_attack_player_location = "first"
                    self_attack_player = player
                    break
            for player in fight.second:
                if isinstance(player, SelfAttackPlayer):
                    self_attack_player_location = "second"
                    self_attack_player = player
                    break
            if self_attack_player_location:
                if self_attack_player_location == "first":
                    if not fight.first_alive and fight.second_alive:
                        fight.first.remove(self_attack_player)
                if self_attack_player_location == "second":
                    if not fight.second_alive and fight.first_alive:
                        fight.second.remove(self_attack_player)

    def handle_taunts(self):
        """处理嘲讽逻辑"""
        all_taunts = []  # 所有嘲讽操作
        die_taunt_operation_type = None  # 要死亡的嘲讽操作
        for p in self.active_players:
            if p.operation.type in {OperationType.TAUNT, OperationType.TAUNT_YEAH, OperationType.TAUNT_FAKE_5}:
                all_taunts.append(p.operation.type)
        if len(set(all_taunts)) in (1, 3):  # 只有一个嘲讽类型或者大杂烩，石头剪刀布平局情况
            return
        if OperationType.TAUNT in all_taunts and OperationType.TAUNT_FAKE_5 in all_taunts:  # 石头vs布
            die_taunt_operation_type = OperationType.TAUNT  # 石头输
        if OperationType.TAUNT in all_taunts and OperationType.TAUNT_YEAH in all_taunts:  # 石头vs剪刀
            die_taunt_operation_type = OperationType.TAUNT_YEAH  # 剪刀输
        if OperationType.TAUNT_YEAH in all_taunts and OperationType.TAUNT_FAKE_5 in all_taunts:  # 剪刀vs布
            die_taunt_operation_type = OperationType.TAUNT_FAKE_5  # 布输
        if not die_taunt_operation_type:
            return  # 平局
        for p in self.active_players:
            if p.operation.type == die_taunt_operation_type:
                p.alive = False

    def handle_dead_players(self):
        """处理战斗中死亡的玩家"""
        for fight in self.fights:
            # 第一方死亡的处理
            if not fight.first_alive:
                for player in fight.first:
                    player.alive = False
                    if isinstance(player, SelfAttackPlayer):
                        player.original_player.alive = False
            # 第二方死亡的处理
            if not fight.second_alive:
                for player in fight.second:
                    player.alive = False
                    if isinstance(player, SelfAttackPlayer):
                        player.original_player.alive = False

    def handle_invincible(self):
        """处理无敌状态"""
        # 无敌玩家不会死亡
        for player in self.players:
            if player.is_invincible:
                player.alive = True
            # 打自己玩家设置原始玩家存活
            elif isinstance(player, SelfAttackPlayer):
                player.original_player.alive = True

    def remove_dead_players(self):
        """从活跃玩家列表中移除死亡的玩家"""
        dead_players = set()
        for player in self.active_players:
            if not player.alive:
                dead_players.add(player)
        self.active_players -= dead_players
        # 双手玩家
        dead_double_hand_players = set()
        for double_hand_player in self.double_hand_active_players:
            # 双手都死亡的双手玩家自己也死
            if not double_hand_player.left_hand.alive and not double_hand_player.right_hand.alive:
                double_hand_player.alive = False
                dead_double_hand_players.add(double_hand_player)
        self.double_hand_active_players -= dead_double_hand_players

    def remove_invincible_token(self):
        """移除无敌标记"""
        for p in self.players:
            p.is_invincible = False

    def check_win(self):
        """检查是否有玩家胜利"""
        # 计算有效玩家数量
        total_effective_players = len(self.active_players) + len(self.double_hand_active_players)
        if total_effective_players == 1:
            self.ending = True
            if self.active_players:
                self.winner = list(self.active_players)[0]  # 只有一个单手玩家胜利
            else:
                self.winner = list(self.double_hand_active_players)[0]  # 只有一个双手玩家胜利
        elif total_effective_players == 0:  # 平局
            self.ending = True
            self.winner = None

    def export_game_state(self):
        """导出当前游戏状态"""
        return GameState(
            rules=self.rules,
            turn=self.turn,
            players=self.players,
            active_players=self.active_players,
            double_hand_players=self.double_hand_players,
            double_hand_active_players=self.double_hand_active_players,
            ending=self.ending,
            winner=self.winner
        )

    def __repr__(self):
        player_str = ", ".join(str(p) for p in self.players)
        active_str = ", ".join(p.name for p in self.active_players)
        return f"回合{self.turn} | 玩家:[{player_str}] | 活跃:[{active_str}]"

    def __hash__(self):
        return hash(self.export_game_state())


class Game:
    """游戏主类"""

    def __init__(self, rules: GameRules):
        self.rules = rules
        self.turn = 0
        self.players: set[Player] = set()
        self.active_players: set[Player] = set()
        self.double_hand_players: set[DoubleHandPlayer] = set()
        self.double_hand_active_players: set[DoubleHandPlayer] = set()
        self.ending = False
        self.winner = None

    def find_player_with_name(self, name: str):
        """根据玩家名称查找玩家对象"""
        for p in self.players | self.double_hand_players:
            if p.name == name:
                return p
        return None

    def add_player(self, name: str, is_double_hand: bool = False, shared_ammo: bool = False, is_ai: bool = False):
        """添加玩家，可选择是否为双手玩家和是否共享弹药"""
        if is_double_hand:
            player = DoubleHandPlayer(name, shared_ammo=shared_ammo)
            self.double_hand_players.add(player)
            self.double_hand_active_players.add(player)
            self.players.add(player.left_hand)
            self.players.add(player.right_hand)
            self.active_players.add(player.left_hand)
            self.active_players.add(player.right_hand)
        else:
            player = Player(name, is_ai=is_ai)
            self.players.add(player)
            self.active_players.add(player)

    def add_operation(self, name: str, operation: Operation, hand: str = "left"):
        """添加玩家操作，对于双手玩家需要指定哪只手

        :param name: 玩家名称
        :param operation: 操作
        :param hand: 对于双手玩家指定左右手
        :raises ValueError: 如果玩家不存在、玩家已死亡或攻击目标已死亡
        """
        player = self.find_player_with_name(name)
        if player is None:
            raise ValueError(f"Player {name} not found")
        self._validate_operation(player)
        # 设置操作
        if isinstance(player, DoubleHandPlayer):
            # 双手玩家
            if hand == "left":
                player.left_hand.operation = operation
            elif hand == "right":
                player.right_hand.operation = operation
            else:
                raise ValueError("Hand must be 'left' or 'right'")
        else:
            # 单手玩家
            player.operation = operation

    def _validate_operation(self, player, hand: str = "left"):
        """验证操作是否有效

        :param player: 玩家对象
        :param hand: 对于双手玩家指定左右手
        :raises ValueError: 如果玩家不存在、玩家已死亡或攻击目标已死亡
        """
        # 检查玩家是否存活
        if isinstance(player, DoubleHandPlayer):
            # 双手玩家：检查指定的手是否存活
            if hand == "left" and not player.left_hand.alive:
                raise ValueError(f"DoubleHandPlayer {player.name}\'s left hand is dead, cannot perform operation")
            if hand == "right" and not player.right_hand.alive:
                raise ValueError(f"DoubleHandPlayer {player.name}\'s right hand is dead, cannot perform operation")
        else:
            # 单手玩家：检查是否存活
            if not player.alive:
                raise ValueError(f"Player {player.name} is dead, cannot perform operation")
        # 检查攻击操作的目标是否存活
        if player.operation.type == OperationType.ATTACK and player.operation.attacks:
            for target in player.operation.attacks.keys():
                if not target.alive:
                    raise ValueError(f"Player {player.name} can't attack dead player {target.name}'")

    def step(self):
        """执行一步"""
        operation_handler = GameHandler(self.export_game_state())
        game_state = operation_handler.calculate()
        self.import_game_state(game_state)

    def import_game_state(self, game_state: GameState):
        """导入游戏状态"""
        self.rules = game_state.rules
        self.turn = game_state.turn
        self.players = game_state.players
        self.active_players = game_state.active_players
        self.double_hand_players = game_state.double_hand_players
        self.double_hand_active_players = game_state.double_hand_active_players
        self.ending = game_state.ending
        self.winner = game_state.winner

    def export_game_state(self):
        """导出当前游戏状态"""
        return GameState(
            rules=self.rules,
            turn=self.turn,
            players=self.players,
            active_players=self.active_players,
            double_hand_players=self.double_hand_players,
            double_hand_active_players=self.double_hand_active_players,
            ending=self.ending,
            winner=self.winner
        )

    def __repr__(self):
        return GameState.__repr__(self)

    def __hash__(self):
        return hash(self.export_game_state())

    def __eq__(self, other):
        return self.export_game_state() == other.export_game_state()
